/* Copyright (C) 2004 Stefan Bellon <sbellon@sbellon.de>
 *
 * This file is part of RemotePrinterFS.
 *
 * RemotePrinterFS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * RemotePrinterFS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <oslib/osfile.h>
#include <oslib/osfind.h>
#include <oslib/osgbpb.h>

#include "config.h"
#include "connection.h"
#include "errors.h"
#include "syslogf.h"
#include "utils.h"

#define BUFSIZE 4096

static const char const   *scrapdir = "<Wimp$ScrapDir>." ShortFilingSystemName;
static char *original_sf = NULL;
static char *spoolfile   = NULL;

static byte buffer[BUFSIZE];
static unsigned int bufidx = 0;

static int print_filetype;

void
set_spool_filename(char *filename)
{
    if (spoolfile)
        free(spoolfile);

    spoolfile = malloc(strlen(scrapdir) + strlen(filename) + 2);
    if (spoolfile)
        sprintf(spoolfile, "%s.%s", scrapdir, filename);
    else
        syslogf(LOG_CRIT, "malloc failed, no memory allocated");

    free(filename);
}

int
spool_open(char *special_field, int length, int filetype)
{
    UNUSED(length);

    int s = -1;
    bufidx = 0;
    print_filetype = filetype;

    if (original_sf)
        free(original_sf);
    original_sf = strdup(special_field);
    
    error = xosfile_create_dir(scrapdir, 0);
    if (error)
        goto cleanup;

    syslogf(LOG_DEBUG,
            "About to open spool file '%s' for writing\n", spoolfile);
    error = xosfind_openoutw(osfind_NO_PATH, spoolfile, NULL, (os_fw*)&s);
    if (error) {
        s = -1;
        goto cleanup;
    }

cleanup:    
    if (error)
        syslogf(LOG_ERR, "Error in spool_open: %s", error->errmess);

    return s;
}

static int
spool_write_buffer(int s, int count)
{
    int unwritten;

    error = xosgbpb_writew((os_fw) s, buffer, count, &unwritten);

    if (error) {
        syslogf(LOG_ERR, "Error in spool_write_buffer: %s", error->errmess);
        return -1;
    }

    return (unwritten > 0) ? -1 : 0;
}

int
spool_write(int s, char *buf, int len)
{
    if (bufidx + len <= BUFSIZE) {
        /* There's space in buffer to put all the new bytes into it */
        memcpy(buffer+bufidx, buf, len);
        bufidx += len;
        
        if (bufidx == BUFSIZE) {
            bufidx = 0;
            return spool_write_buffer(s, BUFSIZE);
        } else {
            return 0;
        }
    } else {
        /* We can't put the new bytes into the buffer, so flush it first ... */
        if (bufidx > 0) {
            int result = spool_write_buffer(s, bufidx);
            if (result == -1)
                return -1;
        }

        /* ... then write the new bytes out immediately, this is just easier
           than putting them into the buffer and not that much of a slowdown */
        int unwritten;
        error = xosgbpb_writew((os_fw) s, (byte const*) buf, len, &unwritten);
        
        if (error) {
            syslogf(LOG_ERR, "Error in spool_write_buffer: %s", error->errmess);
            return -1;
        }

        return (unwritten > 0) ? -1 : 0;
    }
}

int
spool_close(int s)
{
    int handle;
    fileswitch_object_type obj_type;
    bits load_addr;
    bits exec_addr;
    int size;
    int unused;
    int count = 0;
    fileswitch_attr attr;
    byte buf[BUFSIZE];

    /* Flush the buffer */
    if (bufidx > 0) {
        int result = spool_write_buffer(s, bufidx);
        if (result == -1)
            goto cleanup;
    }

    /* Close the spool file */
    error = xosfind_closew(s);
    if (error)
        goto cleanup;
    s = -1;

    /* Print the spool file */
    error = xosfile_read_no_path(spoolfile, &obj_type, &load_addr,
                                 &exec_addr, &size, &attr);
    if (error)
        goto cleanup;

    /* Skip empty spool files */
    if (size == 0) {
        syslogf(LOG_DEBUG, "Spool file is empty, skipping ...");
        goto cleanup;
    }

    /* Find out the original protocol to print to */
    error = detect_protocol(original_sf, size, false);
    if (error)
        goto cleanup;

    syslogf(LOG_DEBUG,
            "About to open spool file '%s' for printing\n", spoolfile);

    handle = connection_open(original_sf, size, print_filetype);
    if (handle == -1)
        goto cleanup;

    error = xosfind_openinw(osfind_NO_PATH, spoolfile, NULL, (os_fw*)&s);
    if (error)
        goto cleanup;

    do {
        error = xosgbpb_readw(s, buf, BUFSIZE, &unused);
        if (error)
            goto cleanup;
        connection_write(handle, (char *) buf, BUFSIZE - unused);
        count += BUFSIZE - unused;
        syslogf(LOG_DEBUG|LOG_INFO, "Spooled %i bytes.", count);
    } while (unused == 0);
    syslogf(LOG_DEBUG|LOG_INFO, "Spooling finished with %i bytes.", count);

    /* close the socket and the spool file */
    connection_close(handle);
    error = xosfind_closew(s);
    if (error)
        goto cleanup;
    s = -1;

cleanup:
    if (error)
        syslogf(LOG_ERR, "Error in spool_close: %s", error->errmess);

    /* close a left-open spool file */
    if (s >= 0)
        xosfind_closew(s);

    /* delete the spool file */
    xosfile_delete(spoolfile, &obj_type, &load_addr, &exec_addr, &size, &attr);

    return error ? -1 : 0;
}
